package com.leyou.user.service;

import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.RegexUtils;
import com.leyou.common.utils.ValidateCodeUtils;
import com.leyou.user.dto.UserDTO;
import com.leyou.user.entity.User;

import com.leyou.user.mapper.UserMapper;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.leyou.common.mq.MQConstants.Exchange.SMS_EXCHANGE_NAME;
import static com.leyou.common.mq.MQConstants.RoutingKey.VERIFY_CODE_KEY;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private AmqpTemplate amqpTemplate;   //消息队列

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;


    /**
     * 用户名或电话号码校验
     * 用户存在，返回false
     * 用户不存在，返回true
     * @param data
     * @param type 类型：1、用户名，2、手机
     */
    public Boolean checkData(String data, Integer type) {

        User user = new User();

        switch (type) {
            case 1:
                user.setUsername(data);
                break;
            case 2:
                user.setPhone(data);
                break;
            default:
                throw new LyException(ExceptionEnum.RESOURCE_NOT_FOUND);
        }

        int count = userMapper.selectCount(user);


        return count==0;

    }

    private final String USER_VERIFY_CODE = "ly:user:verify:code:";

    /**
     * 根据手机号调用短信微服务发送短信，
     * 生成随机验证码并保存在redis中，设置过期时间为5分钟
     *
     * @param phone
     */
    public void sendCode(String phone) {

        if (!RegexUtils.isPhone(phone)){
            throw new LyException(ExceptionEnum.INVALID_PHONE_NUMBER);
        }

        String code = ValidateCodeUtils.generateValidateCode(6).toString();

        Map<String,String> map = new HashMap<>();
        map.put("phone",phone);
        map.put("code",code);

        //交换机，Routingkey,消息体
        amqpTemplate.convertAndSend(SMS_EXCHANGE_NAME,VERIFY_CODE_KEY,map);

        //超时时间一分钟
        redisTemplate.opsForValue().set(USER_VERIFY_CODE + phone,code,5, TimeUnit.MINUTES);
    }

    /**
     * 根据用户名及密码查询用户信息
     * 1、根据用户名查询用户信息
     * 2、如果能查到用户信息就继续往下走，如果不能，就抛出异常
     * 3、根据用户输入的密码与我们从数据库中查出的密码进行匹配  经过解析后 能够匹配的话
     * 4、返回用户信息，包括用户id，用户名，电话号码
     * @param username
     * @param password
     * @return
     */
    public UserDTO queryUserByUsernameAndPassword(String username, String password) {
        // 1根据用户名查询
        User u = new User();
        u.setUsername(username);
        User user = userMapper.selectOne(u);
        // 2判断是否存在
        if (user == null) {
            // 用户名错误
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }

        // 3校验密码  解密  根据用户输入的密码以及数据库中存储的用户密码 进行解密
        if(!passwordEncoder.matches(password, user.getPassword())){
            // 密码错误
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        return BeanHelper.copyProperties(user, UserDTO.class);
    }

    /**
     * 用户注册逻辑方法
     * 1、先查验证码是否正确
     * 2、再对密码加密
     * 3、写入数据库
     * @param user
     * @param code
     */
    @Transactional
    public void register(User user, String code) {

        String key = USER_VERIFY_CODE + user.getPhone();
        //1、
        String redisCode = redisTemplate.opsForValue().get(key);
        //如果redis中没有此验证码，或传入的验证码与redis中的验证码不一致
        if (redisCode==null || !redisCode.equals(code)){
            throw new LyException(ExceptionEnum.INVALID_VERIFY_CODE);
        }

        //2、 加密
        String password = passwordEncoder.encode(user.getPassword());

        user.setPassword(password);

        //3、
        int record = userMapper.insertSelective(user);
        if (record != 1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }


        //redis中有此验证码，并且剩余时间大于三秒  我们就删除它
        if ( redisTemplate.hasKey(key) && redisTemplate.getExpire(key) > 3L ) {

            redisTemplate.delete(key);
        }

    }
}